package com.quick.develop.flowable.application.impl;

import cn.hutool.core.collection.CollUtil;
import com.quick.develop.flowable.constant.BpmConstants;
import com.quick.develop.flowable.convert.instance.BpmInstanceCreateConvert;
import com.quick.develop.flowable.domain.dto.instance.BpmProcessInstanceDTO;
import com.quick.develop.flowable.domain.dto.task.BpmTaskDTO;
import com.quick.develop.flowable.domain.vo.instance.BpmProcessInstanceCancelReqVO;
import com.quick.develop.flowable.domain.vo.instance.BpmProcessInstanceCreateReqVO;
import com.quick.develop.flowable.domain.vo.system.SysDept;
import com.quick.develop.flowable.domain.vo.task.BpmTaskLogReqVO;
import com.quick.develop.flowable.domain.vo.task.BpmTaskReqVO;
import com.quick.develop.flowable.enums.BpmErrorCodeConstants;
import com.quick.develop.flowable.enums.BpmExecuteCmd;
import com.quick.develop.flowable.enums.BpmLogCategory;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.framework.security.utils.SecurityUtils;
import com.quick.develop.flowable.application.IBpmProcessInstanceApplication;
import com.quick.develop.flowable.application.IBpmTaskApplication;
import com.quick.develop.flowable.application.IBpmTaskAssigneeApplication;
import com.quick.develop.flowable.framework.cmd.SetTaskScopeTypeCmd;
import com.quick.develop.flowable.framework.convert.instance.BpmProcessInstanceConvert;
import com.quick.develop.flowable.framework.convert.task.BpmTaskConvert;
import com.quick.develop.flowable.framework.enums.istance.BpmProcessInstanceDeleteReasonEnum;
import com.quick.develop.flowable.framework.factory.FlowServiceFactory;
import com.quick.develop.flowable.service.process.IBpmProcessInstanceService;
import com.quick.develop.flowable.service.task.IBpmActivityService;
import com.quick.develop.flowable.service.task.IBpmCommentService;
import com.quick.develop.flowable.service.task.IBpmHistoricTaskService;
import com.quick.develop.flowable.service.task.IBpmTaskService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.HistoricTaskInstanceQueryImpl;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.PROCESS_INSTANCE_CANCEL_FAIL_CALL_ACTIVITY_UNFINISHED;
import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS;
import static com.quick.develop.framework.common.utils.ServiceExceptionUtils.exception;
import static com.quick.develop.framework.utils.collection.CollectionUtils.convertSet;
import static com.quick.develop.framework.utils.collection.CollectionUtils.filterList;

/**
 * @author junyuan.liu
 * @description:
 * @date 2022/5/30 19:44
 */
@Component
@Slf4j
public class BpmProcessInstanceApplicationImpl extends FlowServiceFactory implements IBpmProcessInstanceApplication {
    @Resource
    private IBpmProcessInstanceService bpmProcessInstanceService;

    @Resource
    private IBpmTaskService bpmTaskService;

    @Resource
    private IBpmTaskApplication bpmTaskApplication;

    @Resource
    private IBpmCommentService bpmCommentService;

    @Resource
    private IBpmTaskAssigneeApplication bpmTaskAssigneeApplication;

    @Resource
    private IBpmActivityService bpmActivityService;

    @Resource
    private IBpmHistoricTaskService bpmHistoricTaskService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcess( BpmProcessInstanceCreateReqVO reqVO) {
        Optional.ofNullable(reqVO.getBusinessKey())
                .orElseThrow(() -> exception(BpmErrorCodeConstants.PROCESS_BUSINESS_KEY_EMPTY));
        String processInstanceId = bpmProcessInstanceService.createProcessInstanceByKey(reqVO);
        Task task = bpmTaskService.getTaskByProcessInstanceId(processInstanceId);
        if (Objects.isNull(task)){
            throw exception(BpmErrorCodeConstants.TASK_IS_NOT_EXISTS);
        }
        reqVO.setTaskId(task.getId());
        reqVO.setProcessInstanceId(processInstanceId);
        reqVO.setAutoComplete(true);
        BpmTaskReqVO taskReqVO = BpmInstanceCreateConvert.INSTANCE.convert(reqVO);
        if (StringUtils.isEmpty(taskReqVO.getComment()) ) {
            taskReqVO.setComment("已办理。");
        }
        if (StringUtils.isEmpty(taskReqVO.getExecuteCmd())) {
            taskReqVO.setExecuteCmd(BpmExecuteCmd.SUBMIT.getCmd());
        }

        bpmTaskApplication.complete(taskReqVO);

        return processInstanceId;
    }

    @Override
    public List<BpmTaskDTO> getProcessLogs(BpmTaskLogReqVO reqVO) {

        HistoricTaskInstanceQuery   hisTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .processInstanceBusinessKey(reqVO.getBusinessKey());
        String queryType = reqVO.getQueryType();

        // 主办 、 协办 、 阅知
        if (Objects.equals(queryType, BpmLogCategory.SPONSORED.code)
                || Objects.equals(queryType, BpmLogCategory.ASSISTANCE.code)
                || Objects.equals(queryType, BpmLogCategory.READ.code)){
            hisTaskInstanceQuery.taskCategory(queryType);
        }
        // 时间
        if (Objects.equals(queryType, BpmLogCategory.TIME.code)) {
            hisTaskInstanceQuery.orderByDueDateNullsFirst()
                    .orderByHistoricTaskInstanceEndTime().desc()
                    .orderByHistoricTaskInstanceStartTime().desc();
        } else {
            hisTaskInstanceQuery.orderByDueDateNullsLast()
                    .orderByHistoricTaskInstanceStartTime().asc()
                    .orderByHistoricTaskInstanceEndTime().asc();
        }


//        List<HistoricTaskInstance> taskList = hisTaskInstanceQuery.list();
        List<HistoricTaskInstance> taskList = bpmHistoricTaskService.getHistoricTaskByBusinessKey((HistoricTaskInstanceQueryImpl) hisTaskInstanceQuery);
        if ( CollUtil.isEmpty(taskList) ) {
            return Collections.emptyList();
        }

        Set<String> users = convertSet(taskList, HistoricTaskInstance::getAssignee);
        Set<String> var = new HashSet<>();
        // 领导查询
        if (Objects.equals(queryType, BpmLogCategory.LEADER.code)) {
            // 公司领导 下的人员
            var = bpmTaskAssigneeApplication.getLeaderUsers();
        }
        // 本部门查询
        else if (Objects.equals(queryType, BpmLogCategory.DEPARTMENT.code)) {
            SysDept dept = SecurityUtils.getDept();
            //  users在本部门下的人员
            var = Optional.ofNullable(dept).map(d -> bpmTaskAssigneeApplication.checkUserDept(d.getDeptId(), users)).orElse(Collections.emptySet()) ;
            // 公司领导
            Set<String> var1 = bpmTaskAssigneeApplication.getLeaderUsers();
            var.addAll(var1);
        }
        if (Objects.equals(queryType, BpmLogCategory.DEPARTMENT.code) || Objects.equals(queryType, BpmLogCategory.LEADER.code)) {
            if (var.isEmpty()) {
                return Collections.emptyList();
            }
            Set<String> finalVar = var;
            taskList = filterList(taskList, r -> finalVar.contains(r.getAssignee()));
        }

        // 过滤后为空
        if (CollUtil.isEmpty(taskList)) {
            return Collections.emptyList();
        }

        // 过滤过的人员数据
        Set<String> resultUsers = convertSet(taskList, HistoricTaskInstance::getAssignee);
        Map<String, String> names = bpmTaskAssigneeApplication.getUsersRealNameByUserCode(resultUsers);

        Set<String> ids = convertSet(taskList, HistoricTaskInstance::getProcessInstanceId);
        Map<String, List<Comment>> comments = bpmCommentService.getTaskCommentMapByProcessInstanceIds(ids);


        return BpmTaskConvert.INSTANCE.convertList4(taskList, comments, names);
    }

    @Override
    public List<BpmTaskDTO> getLogsByProcessInstanceId(String processInstanceId) {
        if(StringUtils.isEmpty(processInstanceId)) {
            return Collections.emptyList();
        }
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceEndTime()
                .desc().list();

        if ( CollUtil.isEmpty(taskList) ) {
            return Collections.emptyList();
        }
        Set<String> users = convertSet(taskList, HistoricTaskInstance::getAssignee);
        Map<String, String> names = bpmTaskAssigneeApplication.getUsersRealNameByUserCode(users);
        Map<String, List<Comment>> comments = bpmCommentService.getTaskCommentMapByProcessInstanceId(processInstanceId);

        return BpmTaskConvert.INSTANCE.convertList4(taskList, comments, names);
    }

    @Override
    public BpmProcessInstanceDTO getMainProcessInstanceByBusinessKey(String businessKey) {
        HistoricProcessInstance instance = bpmProcessInstanceService.getMainProcessInstanceByBusinessKey(businessKey);

        return BpmProcessInstanceConvert.INSTANCE.convertDTO(instance);
    }

    @Override
    public BpmProcessInstanceDTO getMainProcessInstanceByProcessInstanceId(String id) {
        HistoricProcessInstance instance = bpmProcessInstanceService.getMainProcessInstanceByProcessInstanceId(id);

        return BpmProcessInstanceConvert.INSTANCE.convertDTO(instance);
    }

    @Override
    public void cancelProcessInstance(BpmProcessInstanceCancelReqVO cancelReqVO) {
        String userCode = SecurityUtils.getUserCode();

        // 校验流程实例存在
        ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(cancelReqVO.getProcessInstanceId());
        Optional.ofNullable(instance).orElseThrow(() -> exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS));
        // 只能取消自己的 -- WTF
        /*if (!Objects.equals(instance.getStartUserId(), userCode)) {
            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
        }*/
        // 有子流程运行不让终止
        if (!bpmActivityService.isCallActivityFinished(instance.getProcessInstanceId())) {
            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_CALL_ACTIVITY_UNFINISHED);
        }
        List<Task> list = bpmTaskService.getTasksByProcessInstanceId(instance.getProcessInstanceId());

        Optional.ofNullable(list).ifPresent(l -> {
            l.forEach(t -> {
                String id = t.getId();
                if (StringUtils.isNotEmpty(t.getAssignee())) {
                    taskService.setOwner(id, t.getAssignee());
                }
                // 被撤回的任务设置 处理人为当前人
                taskService.setVariableLocal(id, BpmConstants.ENTRUST_IGNORE, BpmConstants.ENTRUST_IGNORE);
                taskService.setAssignee(id, userCode);
                managementService.executeCommand(new SetTaskScopeTypeCmd(id, BpmExecuteCmd.STOP.cmd));
                bpmCommentService.addComment(id, t.getProcessInstanceId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()), BpmExecuteCmd.STOP, null);
            });
        });
        //设置标识
        runtimeService.setVariable(instance.getProcessInstanceId(), BpmConstants.EXECUTE_CMD, BpmExecuteCmd.STOP.cmd);

        // 通过删除流程实例，实现流程实例的取消,
        // 删除流程实例，正则执行任务ACT_RU_TASK. 任务会被删除。通过历史表查询
        runtimeService.deleteProcessInstance(cancelReqVO.getProcessInstanceId(),
                BpmExecuteCmd.STOP.cmd + BpmConstants.COMMENT_SEPARATOR + BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
    }

}
